package com.hzjy.api.service.impl;

import com.alipay.api.AlipayClient;
import com.alipay.api.DefaultAlipayClient;
import com.alipay.api.request.AlipayTradePagePayRequest;
import com.alipay.api.response.AlipayTradePagePayResponse;
import com.hzjy.api.beans.*;
import com.hzjy.api.vo.BqUser;
import com.hzjy.common.config.AlipayConfig;
import com.hzjy.common.config.RuoYiConfig;
import com.hzjy.common.config.WXpayConfig;
import com.hzjy.common.core.domain.AjaxResult;
import com.hzjy.common.enums.DelistingStatusEnum;
import com.hzjy.common.enums.TradingStatusEnum;
import com.hzjy.common.enums.TradingType;
import com.hzjy.common.utils.CharUtil;
import com.hzjy.common.utils.JSONUtil;
import com.hzjy.common.utils.QRCodeUtil;
import com.hzjy.common.utils.ServletUtils;
import com.hzjy.common.utils.file.FileUploadUtils;
import com.hzjy.common.utils.wx.WXPayConstants;
import com.hzjy.common.utils.wx.WXPayUtil;
import org.apache.commons.io.IOUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.mock.web.MockMultipartFile;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.web.multipart.MultipartFile;

import java.io.*;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.HttpURLConnection;
import java.net.URL;
import java.util.*;

import static com.hzjy.common.utils.wx.WXPayUtil.HMACSHA256;
import static com.hzjy.common.utils.wx.WXPayUtil.MD5;

@Service
public class PayServiceImpl {
    private static final Logger log = LoggerFactory.getLogger(PayServiceImpl.class);
    @Autowired
    private HzjyAgreementServiceImpl hzjyAgreementServiceImpl;
    @Autowired
    private HzjyServiceChargeServiceImpl hzjyServiceChargeService;
    @Autowired
    private ApiProductServiceImpl apiProductService;
    @Autowired
    private HzjyTradingnewsServiceImpl hzjyTradingnewsServiceImpl;
    @Autowired
    private HzjyUserBillServiceImpl hzjyUserBillServiceImpl;
    @Autowired
    private HzjyUserPayRecordServiceImpl hzjyUserPayRecordServiceImpl;
    @Autowired
    private BqUserServiceImpl bqUserServiceImpl;
    @Autowired
    private ApiDelistServiceImpl apiDelistService;


    /**
     * 挂牌/摘牌计算费用
     *
     * @param contractId
     * @param userId
     * @return
     */
    @Transactional
    public AjaxResult countTradeCost(Long contractId, String userId) {
        //查询合同信息
        HzjyAgreement hzjyAgreement = hzjyAgreementServiceImpl.selectHzjyAgreementById(contractId);
        if (hzjyAgreement == null) {
            log.error("系统异常,合同信息未获取到.");
            return AjaxResult.error(401, "合同信息未获取到");
        }
        Map priceMap = new HashMap();
        //判断是挂牌还是摘牌 true 摘牌  false挂牌
        Boolean flag = false;
        if (hzjyAgreement.getDelistId() != null){
            flag = true;
        }
        priceMap.put("flag",flag);
        HzjyUserBill queryBill = new HzjyUserBill();
        queryBill.setContractNo(hzjyAgreement.getContractNo());
        queryBill.setProductId(hzjyAgreement.getProductId());
        List<HzjyUserBill> billList = hzjyUserBillServiceImpl.selectHzjyUserBillList(queryBill);
        List<HzjyServiceCharge> priceList = hzjyServiceChargeService.selectHzjyServiceChargeList(null);
        log.info("查询设置的手续费:" + JSONUtil.list2json(priceList));
        BigDecimal total = new BigDecimal(BigInteger.ZERO);
        if (CollectionUtils.isEmpty(priceList)) {
            log.error("系统异常,价格模板未设置.");
            return AjaxResult.error("系统异常,价格模板未设置");
        }
        HzjyServiceCharge charge = priceList.get(0);
        priceMap.put("signPrice", charge.getSignPrice());
        HzjyProduct hzjyProduct = apiProductService.selectProductInfo(Long.valueOf(hzjyAgreement.getProductId()));
        if (CollectionUtils.isEmpty(billList)) {
            log.info("【支付,未生成过账单,开始生成账单】");
            total = total.add(charge.getSignPrice());
            log.info("查询作品价格:" + JSONUtil.object2json(hzjyProduct));
            if (hzjyProduct != null) {
                double price = 0;
                double rate = 0;
                if (hzjyAgreement.getType() == 1) {
                    price = hzjyProduct.getPrice() * 0.01;
                    rate = charge.getTradingPrice() * 0.01;
                } else if (hzjyAgreement.getType() == 2) {
                    HzjyDelist hzjyDelist = apiDelistService.selectHzjyDelistById(hzjyAgreement.getDelistId());
                    price = Double.valueOf(hzjyDelist.getPriceTable());
                    rate = charge.getDelistingPrice() * 0.01;
                }
                log.info("手续费比例:" + rate);
                if (price > 0) {
                    log.info("作品价格：" + price);
                    double other = price * rate;
                    log.info("手续费：" + other);
                    total = total.add(new BigDecimal(String.valueOf(other)));
                    total = total.setScale(2,BigDecimal.ROUND_UP);
                    BigDecimal otherPrice = new BigDecimal(String.valueOf(other)).setScale(2,BigDecimal.ROUND_UP);
                    log.info("手续费2：" + otherPrice);
                    priceMap.put("otherPrice", otherPrice);
                }
            }
            priceMap.put("totalPrice", total);
            log.info("计算的支付费用：" + total);
            HzjyUserBill hzjyUserBill = new HzjyUserBill();
            hzjyUserBill.setContractNo(hzjyAgreement.getContractNo());
            hzjyUserBill.setProductId(hzjyAgreement.getProductId());
            hzjyUserBill.setDealPrice(total);
            hzjyUserBill.setDealTime(new Date());
            hzjyUserBill.setStatus(0);
            hzjyUserBill.setUserId(userId);
            hzjyUserBill.setOrderNo(hzjyUserBillServiceImpl.createOrderNumber(hzjyProduct.getTradingType() == 1 ?
                    TradingType.G.getInfo() : TradingType.Z.getInfo(), contractId));
            hzjyUserBill.setContractId(contractId);
            hzjyUserBillServiceImpl.insertHzjyUserBill(hzjyUserBill);
        } else {
            priceMap.put("totalPrice", billList.get(0).getDealPrice());
            priceMap.put("otherPrice", billList.get(0).getDealPrice().subtract(charge.getSignPrice()));
            HzjyUserBill newBill = billList.get(0);
            newBill.setOrderNo(hzjyUserBillServiceImpl.createOrderNumber(hzjyProduct.getTradingType() == 1 ?
                    TradingType.G.getInfo() : TradingType.Z.getInfo(), contractId));
            hzjyUserBillServiceImpl.updateHzjyUserBill(newBill);
            log.info("【支付,已生成过账单,不再重新生成账单】");
        }
        log.info("账单要支付的费用为：" + JSONUtil.map2json(priceMap));
        return AjaxResult.success("费用计算完成", priceMap);
    }

    /**
     * 挂牌支付成功业务处理
     *
     * @param orderNo
     * @param payType 1 微信 2支付宝 3 银行卡
     */
    @Transactional
    public void PaySuccess(String orderNo, Integer payType) throws Exception {
        HzjyUserBill queryBill = new HzjyUserBill();
        queryBill.setOrderNo(orderNo);
        List<HzjyUserBill> billList = hzjyUserBillServiceImpl.selectHzjyUserBillList(queryBill);
        if (CollectionUtils.isEmpty(billList)) {
            log.error("系统异常,未获取到订单信息.");
            throw new Exception("系统异常,未获取到订单信息");
        }
        //查询合同信息
        HzjyAgreement hzjyAgreement = hzjyAgreementServiceImpl.selectHzjyAgreementById(billList.get(0).getContractId());
        if (hzjyAgreement == null) {
            log.error("系统异常,合同信息未获取到.");
            throw new Exception("系统异常,合同信息未获取到");
        }
        if (hzjyAgreement.getType() == 1) {
            log.info("【更新挂牌成功并上架】");
            HzjyProduct hzjyProduct = apiProductService.selectProductInfo(Long.valueOf(hzjyAgreement.getProductId()));
            hzjyProduct.setState(TradingStatusEnum.TradingSuccess.getCode());
            hzjyProduct.setUpState(1);//默认挂牌成功上架
            hzjyProduct.setPutawayTime(new Date());
            apiProductService.updateHzjyProduct(hzjyProduct);
            log.info("【支付成功，新建挂牌成功资讯】.");
            HzjyTradingnews hzjyTradingnews = new HzjyTradingnews();
            hzjyTradingnews.setContent("恭喜挂牌成功！");
            hzjyTradingnews.setTitle("恭喜" + hzjyProduct.getProductName() + "挂牌成功.");
            hzjyTradingnews.setProductId(hzjyAgreement.getProductId());
            hzjyTradingnews.setTime(new Date());
            hzjyTradingnewsServiceImpl.insertHzjyTradingnews(hzjyTradingnews);
        } else if (hzjyAgreement.getType() == 2) {
            log.info("【摘牌付款成功，等待挂牌方确认收款.】");
            //更新摘牌申请表状态
            HzjyDelist hzjyDelist = apiDelistService.selectHzjyDelistById(hzjyAgreement.getDelistId());
            if (hzjyDelist == null) {
                log.error("【摘牌申请未找到，系统异常.】");
                throw new Exception("摘牌申请未找到，系统异常");
            }
            hzjyDelist.setStatus(DelistingStatusEnum.PaySuccess.getCode());
            apiDelistService.updateHzjyDelist(hzjyDelist);
        }
        log.info("【支付成功,更新账单】");
        Date dealTime = new Date();
        HzjyUserBill bill = billList.get(0);
        bill.setStatus(1);
        bill.setDealTime(dealTime);
        hzjyUserBillServiceImpl.updateHzjyUserBill(bill);
        log.info("【支付成功，创建支付记录.】");
        HzjyUserPayRecord hzjyUserPayRecord = new HzjyUserPayRecord();
        hzjyUserPayRecord.setContractNo(hzjyAgreement.getContractNo());
        hzjyUserPayRecord.setDealTime(dealTime);
        hzjyUserPayRecord.setProductId(hzjyAgreement.getProductId());
        hzjyUserPayRecord.setPayMoney(bill.getDealPrice());
        hzjyUserPayRecord.setPayType(payType);
        hzjyUserPayRecord.setUserId(hzjyAgreement.getUserId());
        //开票状态
        hzjyUserPayRecord.setStatus(0);
        hzjyUserPayRecordServiceImpl.insertHzjyUserPayRecord(hzjyUserPayRecord);
    }

    /**
     * 微信支付生成二维码
     *
     * @param contractId
     * @param price
     * @return
     * @throws Exception
     */
    public String getPayUrl(String contractId, String price) throws Exception {
        //查询合同信息
        HzjyAgreement hzjyAgreement = hzjyAgreementServiceImpl.selectHzjyAgreementById(Long.valueOf(contractId));
        if (hzjyAgreement == null) {
            log.error("系统异常,合同信息未获取到.");
            throw new Exception("系统异常,合同信息未获取到.");
        }
        HzjyProduct hzjyProduct = apiProductService.selectProductInfo(Long.valueOf(hzjyAgreement.getProductId()));
        HzjyUserBill queryBill = new HzjyUserBill();
        queryBill.setProductId(hzjyAgreement.getProductId());
        queryBill.setContractId(Long.valueOf(contractId));
        List<HzjyUserBill> billList = hzjyUserBillServiceImpl.selectHzjyUserBillList(queryBill);
        if (CollectionUtils.isEmpty(billList)) {
            log.error("系统异常,未获取到订单信息.");
            throw new Exception("系统异常,未获取到订单信息.");
        }
        //生成订单对象
        Map<String, String> map = new HashMap<>();
        map.put("appid", WXpayConfig.APPID);//公众账号ID
        map.put("mch_id", WXpayConfig.WXPAYMENTACCOUNT);//商户号
        map.put("body", hzjyProduct.getProductName());//商品描述
        map.put("nonce_str", "test");
        map.put("notify_url", WXpayConfig.notify_url);//通知地址
        map.put("out_trade_no", billList.get(0).getOrderNo());//订单号
        map.put("spbill_create_ip", ServletUtils.getRequest().getRemoteAddr());//终端ip
        map.put("trade_type", "NATIVE");//交易类型
        Double totalFee = Double.valueOf(price) * 100;
        map.put("total_fee", String.valueOf(totalFee.intValue()));//总金额
        map.put("fee_type", "CNY");
        String sign = createSign(map, WXpayConfig.APIKEY);//调用生成签名的方法，用以Map集合中的相关参数生成签名
        map.put("sign", sign);//签名
        String orderInfo = new String(WXPayUtil.mapToXml(map).getBytes("UTF-8"));
        log.info("【微信统一支付参数】:" + orderInfo);
        Map<String, String> stringStringMap = httpOrder(orderInfo);//调用统一下单接口
        String code_url = stringStringMap.get("code_url");
        log.info("【微信预付款地址】：" + code_url);
        File f = QRCodeUtil.encode(code_url, RuoYiConfig.getProfile(), false);//调用生成二维码的方法
        // 上传文件路径
        String filePath = RuoYiConfig.getUploadPath();
        // 上传并返回新文件名称
        FileInputStream inputStream = new FileInputStream(f);
        MultipartFile multipartFile = new MockMultipartFile("file", f.getName(), "text/plain", IOUtils.toByteArray(inputStream));
        return FileUploadUtils.upload(filePath, multipartFile);
    }

    /**
     * 调统一下单API
     *
     * @param orderInfo
     * @return
     */
    private Map<String, String> httpOrder(String orderInfo) throws Exception {
        String url = WXpayConfig.basePath;

        HttpURLConnection conn = (HttpURLConnection) new URL(url).openConnection();
        //加入数据
        conn.setRequestMethod("POST");
        conn.setDoOutput(true);

        BufferedOutputStream buffOutStr = new BufferedOutputStream(conn.getOutputStream());
        buffOutStr.write(orderInfo.getBytes("UTF-8"));
        buffOutStr.flush();
        buffOutStr.close();

        //获取输入流
        BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream(), "UTF-8"));

        String line = null;
        StringBuffer sb = new StringBuffer();
        while ((line = reader.readLine()) != null) {
            sb.append(line);
        }
        Map<String, String> map = WXPayUtil.xmlToMap(sb.toString());
        log.info("请求微信预付款地址返回结果：" + JSONUtil.map2json(map));
        //返回字段很多，这里只取我们所需的字段
        String return_msg = map.get("return_msg");//返回信息
        String return_code = map.get("return_code");//状态码
        String result_code = map.get("result_code");//业务结果
        String code_url = map.get("code_url");
        //根据微信文档return_code 和result_code都为SUCCESS的时候才会返回code_url
        if (null != map && "SUCCESS".equals(return_code) && "SUCCESS".equals(result_code)) {
            return map;
        } else {
            return null;
        }
    }

    /**
     * 生成签名
     * 这个方法是从微信sdk里copy过来的，自己也可以写，要注意生成签名后UTF-8的转换，要不然容易报签名Body UTF-8错误
     *
     * @param data 待签名数据
     * @param key  API密钥
     */
    public static String createSign(final Map<String, String> data, String key) throws Exception {
        return createSign(data, key, WXPayConstants.SignType.MD5);
    }

    /**
     * 生成签名. 注意，若含有sign_type字段，必须和signType参数保持一致。
     *
     * @param data     待签名数据
     * @param key      API密钥
     * @param signType 签名方式
     * @return 签名
     */
    private static String createSign(final Map<String, String> data, String key, WXPayConstants.SignType signType) throws Exception {
        //根据规则创建可排序的map集合
        Set<String> keySet = data.keySet();
        String[] keyArray = keySet.toArray(new String[keySet.size()]);
        Arrays.sort(keyArray);
        StringBuilder sb = new StringBuilder();
        for (String k : keyArray) {
            if (k.equals(WXPayConstants.FIELD_SIGN)) {
                continue;
            }
            if (data.get(k).trim().length() > 0) // 参数值为空，则不参与签名
                sb.append(k).append("=").append(data.get(k).trim()).append("&");
        }
        sb.append("key=").append(key);
        //转换UTF-8
        String str = new String(sb.toString().getBytes("UTF-8"));
        if (WXPayConstants.SignType.MD5.equals(signType)) {
            return MD5(sb.toString()).toUpperCase();
        } else if (WXPayConstants.SignType.HMACSHA256.equals(signType)) {
            return HMACSHA256(sb.toString(), key);
        } else {
            throw new Exception(String.format("Invalid sign_type: %s", signType));
        }
    }

    /**
     * 小程序调起支付
     *
     * @param contractId
     * @param price
     * @param openId
     * @throws Exception
     */
    public AjaxResult getWxPay(String contractId, String price, String openId) throws Exception {
        String nonce_str = CharUtil.getRandomString(32);
        //查询合同信息
        HzjyAgreement hzjyAgreement = hzjyAgreementServiceImpl.selectHzjyAgreementById(Long.valueOf(contractId));
        if (hzjyAgreement == null) {
            log.error("系统异常,合同信息未获取到.");
            throw new Exception("系统异常,合同信息未获取到.");
        }
        HzjyProduct hzjyProduct = apiProductService.selectProductInfo(Long.valueOf(hzjyAgreement.getProductId()));
        HzjyUserBill queryBill = new HzjyUserBill();
        queryBill.setProductId(hzjyAgreement.getProductId());
        queryBill.setContractId(Long.valueOf(contractId));
        List<HzjyUserBill> billList = hzjyUserBillServiceImpl.selectHzjyUserBillList(queryBill);
        if (CollectionUtils.isEmpty(billList)) {
            log.error("系统异常,未获取到订单信息.");
            throw new Exception("系统异常,未获取到订单信息.");
        }

        price = new BigDecimal(price).multiply(new BigDecimal(100)).stripTrailingZeros().toPlainString();
        //生成订单对象
        Map<String, String> packageParams = new HashMap<String, String>();
        packageParams.put("appid", WXpayConfig.APPID);
        packageParams.put("mch_id", WXpayConfig.WXPAYMENTACCOUNT);
        packageParams.put("nonce_str", nonce_str);
        packageParams.put("body", hzjyProduct.getProductName());//商品描述
        packageParams.put("out_trade_no", billList.get(0).getOrderNo());//商户订单号
        packageParams.put("total_fee", price);//支付金额，这边需要转成字符串类型，否则后面的签名会失败
        packageParams.put("spbill_create_ip", ServletUtils.getRequest().getRemoteAddr());
        packageParams.put("notify_url", WXpayConfig.notify_url);
        packageParams.put("trade_type", "JSAPI");
        packageParams.put("openid", openId);//用户标识

        String sign = createSign(packageParams, WXpayConfig.APIKEY);//调用生成签名的方法，用以Map集合中的相关参数生成签名
        log.info("=======================第一次签名：" + sign + "=====================");
        packageParams.put("sign", sign);//签名
        String orderInfo = new String(WXPayUtil.mapToXml(packageParams).getBytes("UTF-8"));

        try {
            //调用统一下单接口，并接受返回的结果
            Map<String, String> map = httpOrder(orderInfo);
            log.info("【微信统一支付参数】:" + map);
            String return_code = map.get("return_code");//返回状态码
            //返回给移动端需要的参数
            Map<String, String> response = new HashMap<String, String>();
            if (return_code == "SUCCESS" || return_code.equals(return_code)) {
                // 业务结果
                String prepay_id = (String) map.get("prepay_id");//返回的预付单信息
                response.put("appId", WXpayConfig.APPID);
                response.put("nonceStr", nonce_str);
                response.put("package", "prepay_id=" + prepay_id);
                Long timeStamp = System.currentTimeMillis() / 1000;
                response.put("timeStamp", timeStamp + "");//这边要将返回的时间戳转化成字符串，不然小程序端调用wx.requestPayment方法会报签名错误
                response.put("signType", "MD5");
                //String stringSignTemp = "appId=" + WXpayConfig.APPID + "&nonceStr=" + nonce_str + "&package=prepay_id=" + prepay_id + "&signType=" + "MD5" + "&timeStamp=" + timeStamp;
                //再次签名，这个签名用于小程序端调用wx.requesetPayment方法
                //String paySign = WXPayUtil.MD5(stringSignTemp);
                String paySign = createSign(response, WXpayConfig.APIKEY);
                response.put("paySign", paySign);

                log.info("=======================第二次签名：" + paySign + "=====================");
            }
            return AjaxResult.success(response);
        } catch (Exception ex) {
            ex.printStackTrace();
            return AjaxResult.error(500, "支付调起失败");
        }
    }

    /**
     * 查询支付结果
     *
     * @param contractId
     * @param userId
     * @return
     */
    public boolean payResult(String contractId, String userId) {
        HzjyUserBill queryBill = new HzjyUserBill();
        queryBill.setContractId(Long.valueOf(contractId));
        queryBill.setUserId(userId);
        List<HzjyUserBill> billList = hzjyUserBillServiceImpl.selectHzjyUserBillList(queryBill);
        if (!CollectionUtils.isEmpty(billList)) {
            if (billList.get(0).getStatus() == 1) {
                return true;
            }
        }
        return false;
    }

    /**
     * pc调用支付宝支付
     * @param contractId
     * @param price
     * @return
     */
    public String getAliPay(String contractId, String price) throws Exception {
        AlipayClient alipayClient = new DefaultAlipayClient(AlipayConfig.ALIPAY_URL, AlipayConfig.APPID, AlipayConfig.MERCHANT_PRIVATE_KEY, "json", AlipayConfig.charset, AlipayConfig.ALIPAY_PUBLIC_KEY, AlipayConfig.sign_type);
        AlipayTradePagePayRequest request = new AlipayTradePagePayRequest();
        request.setReturnUrl(AlipayConfig.return_url);
        request.setNotifyUrl(AlipayConfig.NOTIFY_URL);
        //查询合同信息
        HzjyAgreement hzjyAgreement = hzjyAgreementServiceImpl.selectHzjyAgreementById(Long.valueOf(contractId));
        if (hzjyAgreement == null) {
            log.error("系统异常,合同信息未获取到.");
            throw new Exception("系统异常,合同信息未获取到.");
        }
        HzjyProduct hzjyProduct = apiProductService.selectProductInfo(Long.valueOf(hzjyAgreement.getProductId()));
        HzjyUserBill queryBill = new HzjyUserBill();
        queryBill.setProductId(hzjyAgreement.getProductId());
        queryBill.setContractId(Long.valueOf(contractId));
        List<HzjyUserBill> billList = hzjyUserBillServiceImpl.selectHzjyUserBillList(queryBill);
        if (CollectionUtils.isEmpty(billList)) {
            log.error("系统异常,未获取到订单信息.");
            throw new Exception("系统异常,未获取到订单信息.");
        }
        request.setBizContent("{" +
                "\"out_trade_no\":\"" + billList.get(0).getOrderNo() + "\"," +
                "\"product_code\":\"FAST_INSTANT_TRADE_PAY\"," +
                "\"total_amount\":" + price + "," +
                "\"subject\":\"" + hzjyProduct.getProductName() + "\"," +
                "\"timeout_express\":\"15m\"" +
                "  }");
        AlipayTradePagePayResponse response = alipayClient.pageExecute(request);
        log.info("-----------支付宝返回参数--------------"+response);
        if (response.isSuccess()) {
            System.out.println("调用成功");
        } else {
            System.out.println("调用失败");
        }
        return response.getBody();
    }
}
